home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
admin
/
linuxcon.000
/
linuxcon
/
linuxconf-1.6
/
netconf
/
routes.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-07-19
|
11KB
|
482 lines
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include "netconf.h"
#include "../paths.h"
#include "../xconf/xconf.h"
#include "netconf.m"
extern CONFIG_FILE f_conf_routes;
/*
Parse the argument of a route add command (read from ETC_CONF_ROUTES
Return -1 if any error.
*/
static int route_parsecmd (
const char *buf,
int noline,
SSTRING &ip_dst,
SSTRING &gateway,
SSTRING &netmask,
SSTRING &flags)
{
int ret = 0;
char words[8][100];
netmask.setfrom("");
words[0][0] = words[1][0] = words[2][0] = words[3][0] = '\0';
words[4][0] = words[5][0] = words[6][0] = words[7][0] = '\0';
sscanf (buf,"%s %s %s %s %s %s %s %s"
,words[0],words[1],words[2],words[3]
,words[4],words[5],words[6],words[7]);
int i=0;
int seen_host = 0;
if (strcmp(words[0],"-net")==0){
flags.setfrom ("UG");
i++;
}else if (strcmp(words[0],"-host")==0){
flags.setfrom ("UGH");
seen_host = 1;
i++;
}else{
flags.setfrom ("UG");
}
if (strcmp(words[i],"default")==0
|| device_validip(words[i],seen_host)){
ip_dst.setfrom (words[i]);
}else{
struct netent *ent = getnetbyname (words[i]);
if (ent == NULL){
xconf_error (MSG_U(E_IVLDEST
,"Invalid destination %s for line %d in file %s\n%s\n")
,words[i],noline,ETC_CONF_ROUTES,buf);
ret = -1;
}else{
devices_ip2a(ent,ip_dst);
}
}
i++;
if (strcmp(words[i],"gw")!=0){
xconf_error (MSG_U(E_NOKEYGW
,"Keyword gw missing from line %d of file %s\n%s")
,noline,ETC_CONF_ROUTES,buf);
ret = -1;
}else{
i++;
if (device_validip(words[i],1)){
gateway.setfrom (words[i]);
}else{
struct hostent *ent = gethostbyname (words[i]);
if (ent == NULL){
xconf_error (MSG_U(E_IVLGTW
,"Invalid gateway %s for line %d in file %s\n%s\n")
,words[i],noline,ETC_CONF_ROUTES,buf);
ret = -1;
}else{
devices_ip2a (ent,gateway);
}
}
i++;
}
if (words[i][0] != '\0'){
if (strcmp(words[i],"netmask")!=0){
xconf_error (MSG_U(E_NETMASK
,"Keyword netmask expected, on line %d of file %s\n%s")
,noline,ETC_CONF_ROUTES,buf);
ret = -1;
}else{
i++;
netmask.setfrom (words[i]);
}
}
return ret;
}
/*
Reformat a command (reverse route_parsecmd)
*/
static void route_formatcmd(
const SSTRING &ip_dst,
const SSTRING &gateway,
const SSTRING &netmask,
const SSTRING &flags,
char *buf)
{
buf[0] = '\0';
if (flags.cmp("UG")==0){
strcat (buf,"-net ");
}else{
strcat (buf,"-host ");
}
buf += strlen(buf);
buf += sprintf(buf,"%s gw %s",ip_dst.get(),gateway.get());
if (!netmask.is_empty()){
sprintf (buf," netmask %s",netmask.get());
}
}
/*
Control a route in the kernel
*/
PUBLIC ROUTE::ROUTE (
const char *dst,
const char *gate,
const char *mask,
const char *_flags,
const char *_iface)
{
ip_dst.setfrom (dst);
ip_gateway.setfrom (gate);
netmask.setfrom (mask);
flags.setfrom (_flags);
iface.setfrom (_iface);
tag = 0;
}
PUBLIC ROUTE::ROUTE (
const char *buf,
int noline) // Help generate error message
{
if (route_parsecmd(buf,noline,ip_dst,ip_gateway,netmask,flags)==-1){
invalid_line.setfrom (buf);
}
tag = 0;
}
PUBLIC ROUTE::ROUTE ()
{
tag = 0;
}
PUBLIC ROUTE::~ROUTE ()
{
}
/*
Format and output in a file like ETC_CONF_ROUTE
Output a line only if it contain either a valid info or an
invalid (unparsable) line. So it does not generate empty line.
*/
PUBLIC void ROUTE::write (FILE *fout)
{
if (!invalid_line.is_empty()){
fprintf (fout,"%s\n",invalid_line.get());
}else if (!ip_dst.is_empty()){
char buf[300];
route_formatcmd (ip_dst,ip_gateway,netmask,flags,buf);
fprintf (fout,"%s\n",buf);
}
}
/*
Return a flag recorded by settag. This allows an application
to mark a ROUTE as seen or ok or not ok and later, get the
flag back. THis flag has no internal use for ROUTE. It is just
kind enough to store it.
*/
PUBLIC int ROUTE::gettag ()
{
return tag;
}
PUBLIC void ROUTE::settag (int _tag)
{
tag = _tag;
}
/*
Return != 0 if this route is simply the route to the localnet
without any gateway.
*/
PUBLIC int ROUTE::isdevice ()
{
return ip_gateway.cmp("*")==0;
}
/*
Get the interface (eth0) used for a route
*/
PUBLIC const char *ROUTE::getiface ()
{
return iface.get();
}
/*
Return the gateway of a route.
*/
PUBLIC const char *ROUTE::getgateway()
{
return isdevice() ? iface.get() : ip_gateway.get();
}
/*
Return the destination of a route.
*/
PUBLIC const char *ROUTE::getdst()
{
return ip_dst.get();
}
/*
Return if the destination is a host or a network.
Return != 0 if it is a host.
*/
PUBLIC int ROUTE::dst_is_host()
{
return flags.strchr('H')!=NULL;
}
/*
Tell is a route match a destination.
Return != 0 if true.
*/
PUBLIC int ROUTE::match(const SSTRING &dst)
{
return ip_dst.cmp(dst)==0;
}
/*
Tell if two routes are the same.
Return 0 if different destination.
1 if same destination but different configuration.
2 if exactly the same.
*/
PUBLIC int ROUTE::compare(
const SSTRING &dst,
const SSTRING &gateway,
const SSTRING &o_netmask, // may be an empty string, in this
// case it match anything
const SSTRING &o_flags)
{
int ret = 0;
if (ip_dst.cmp(dst) == 0){
ret = 1;
if (ip_gateway.cmp(gateway)==0
&& (o_netmask.is_empty() || netmask.cmp(o_netmask)==0)
&& flags.cmp(o_flags)==0){
ret = 2;
}
}
return ret;
}
/*
Delete a route from the kernel routing table.
Returne -1 if any error.
*/
PUBLIC int ROUTE::kill ()
{
char cmd[100];
sprintf (cmd,"del %s",ip_dst.get());
return netconf_system_if ("route",cmd);
}
/*
Indicate if a route define the loopback device
*/
PUBLIC int ROUTE::is_loopback()
{
return ip_dst.cmp("127.0.0.1")==0 && ip_gateway.cmp("*")==0;
}
/*
Indicate if a route define the default route
*/
PUBLIC int ROUTE::is_default()
{
return ip_dst.cmp("default")==0;
}
/*
read all the routes currently active
*/
PUBLIC int ROUTES::readactive ()
{
FILE *fin = popen ("/sbin/route -n","r");
int ret = -1;
if (fin == NULL){
xconf_error (MSG_U(E_CANTXROUTE
,"Can't execute command /sbin/route\n"
"No way to update the route table\n"));
}else{
char buf[300];
/* #Specification: route / /sbin/route
netconf use /sbin/route -n to read the route table.
It read only the first four fields (destination, gateway
genmask and Flags) are read. The genmask is ignored
*/
// Skip the first 2 lines
fgets(buf,sizeof(buf)-1,fin);
fgets(buf,sizeof(buf)-1,fin);
while (fgets(buf,sizeof(buf)-1,fin)!=NULL){
char ip_dst[20],ip_gate[20],gen_mask[20],flags[20];
char junk[20],iface[20];
if (sscanf (buf,"%s %s %s %s %s %s %s %s",ip_dst,ip_gate
,gen_mask,flags,junk,junk,junk,iface)!=8){
xconf_error (MSG_U(E_IVLOUTPUT
,"Invalid output from /sbin/route\n%s\n")
,buf);
}else{
ROUTE *rt = new ROUTE (ip_dst,ip_gate,gen_mask,flags,iface);
if (rt != NULL) add (rt);
}
}
pclose (fin);
}
return ret;
}
PUBLIC void ROUTES::write (FILE *fout)
{
for (int i=0; i<getnb(); i++) getitem(i)->write (fout);
}
/*
Get one ROUTE of the table or NULL
*/
PUBLIC ROUTE *ROUTES::getitem(int no) const
{
return (ROUTE*)ARRAY::getitem(no);
}
/*
Locate a ROUTE in the routing table.
Return NULL if it does not exist.
*/
PUBLIC ROUTE *ROUTES::find (const SSTRING &ip_dst)
{
ROUTE *ret = NULL;
int nb = getnb();
for (int i=0; i<nb; i++){
ROUTE *pt = getitem(i);
if (pt->match(ip_dst)){
ret = pt;
break;
}
}
return ret;
}
extern NETCONF_HELP_FILE help_routes;
static CONFIG_FILE f_var_run_current (VAR_RUN_ROUTES_CURRENT
,help_routes,CONFIGF_MANAGED|CONFIGF_OPTIONNAL|CONFIGF_ERASED);
/*
Read all route info stored in VAR_RUN_ROUTES_CURRENT
*/
PUBLIC void ROUTES::readbyme()
{
FILE *fin = f_var_run_current.fopen("r");
if (fin != NULL){
char buf[400];
int noline = 0;
while (fgets(buf,sizeof(buf)-1,fin)){
noline++;
add (new ROUTE (buf,0));
}
fclose (fin);
}
}
/*
Store all route set during this session in VAR_RUN_ROUTES_CURRENT
*/
PUBLIC void ROUTES::writebyme()
{
FILE *fout = f_var_run_current.fopen("w");
if (fout != NULL){
write (fout);
fclose (fout);
}
}
/*
Read the /etc/conf.routes and install/correct all routes.
New routes are added, current routes are validated and corrected
if need (deleted, reinstall), and obsolete route are removed.
Return -1 if any error.
*/
int route_install ()
{
int ret = -1;
/* #Specification: ETC_CONF_ROUTES / optionnal
The file ETC_CONF_ROUTES is optionnal. It means no extra
routes (only route to local network) will be set if missing.
*/
ROUTES active;
active.readactive();
ROUTES lasttime; // List of routes established by this program
// during a different session
lasttime.readbyme();
ROUTES thistime; // Routes that will be set or accepted this
// time
FILE *fin = f_conf_routes.fopen ("r");
if (fin != NULL){
char buf[300];
int noline;
while (fgets_strip (buf,sizeof(buf)-1,fin,&noline)!=NULL){
/* #Specification: ETC_CONF_ROUTES / format
The format of /etc/conf.routes is simply the end
of a route command (everything after the add keyword).
So if you want to install a default route (and want
to do so by editing /etc/conf.routes manually, then you
write
-net default gw router
You only write routes which are not associated to devices.
The primary route to the ethernet network is taken
care automaticly by netconf. You just place extra routes
here.
*/
SSTRING ip_dst,gateway,netmask,flags;
if(route_parsecmd (buf,noline,ip_dst,gateway
,netmask,flags) != -1){
ROUTE * rt = active.find (ip_dst);
int add = 0;
if (rt == NULL){
add = 1;
}else if(lasttime.find(ip_dst)!=NULL){
// This route was set by me, check if it has
// to be updated
rt->settag(1);
if (rt->compare (ip_dst,gateway,netmask,flags) != 2){
rt->kill();
add = 1;
}else{
// This route is still valid, must be written
// back into /var/run/routes.current
thistime.add (new ROUTE (buf,0));
}
}
/* #Specification: netconf / update routes
netconf will only update a route or delete it if
it is not needed any more or is slightly different.
This means that you should not see any network
problem if you add a new route and run netconf --update
*/
if (add){
char cmd[300];
sprintf (cmd,"add %s",buf);
ret = netconf_system_if ("route",cmd);
thistime.add (new ROUTE (buf,0));
}
}
}
fclose (fin);
}
/* #Specification: netconf / update routes / routed
netconf kills any route not found any more in ETC_CONF_ROUTES
This may cause a problem to routed. To avoid it, netconf
will only kill the routes it have setup itself.
Comments welcome.
*/
for (int i=0; i<active.getnb(); i++){
ROUTE *pt = active.getitem(i);
if (pt->gettag()==0
&& lasttime.find(pt->getdst())!=NULL
&& !pt->isdevice()) pt->kill();
}
if (!simul_ison())thistime.writebyme();
return ret;
}
/*
Check if a route is active to a destination
*/
int route_isactive (
const char *dest,
char *gateway)
{
ROUTES active;
active.readactive();
ROUTE *rt = active.find(dest);
if (rt != NULL) strcpy (gateway,rt->getgateway());
return rt != NULL;
}